// package: rewords-server-2024
// filename: ztime
// author: diogoxiang@gmail.com
// time: 2024/7/31 上午11:13

package ztime

import (
	"fmt"
	"math"
	"time"

	"github.com/gogf/gf/v2/frame/g"
	"github.com/gogf/gf/v2/os/gtime"
	"go.mongodb.org/mongo-driver/bson/primitive"
)

const (
	// 1秒
	oneS = time.Second

	// 1分钟 == 60 秒
	oneM = 60 * time.Second

	// 1小时 == 60*60秒
	oneH = 60 * 60 * time.Second

	// 1天 == 24*60*60秒
	oneD = 24 * 60 * 60 * time.Second

	// 一天的 layout start
	layoutStart = "Y-m-d 00:00:00"
	// 一天的 layout end
	layoutEnd = "Y-m-d 23:59:59"
	// 一天的 layout day
	layoutDay = "Y-m-d"

	// 一天的 layout start one minute
	layoutStartOne = "Y-m-d 00:02:00"

	// 小时的
	layoutHourStime = "Y-m-d H:00:00"
	layoutHourEtime = "Y-m-d H:59:59"

	// 使用更简洁的命名
	Second = time.Second
	Minute = 60 * time.Second
	Hour   = 60 * Minute
	Day    = 24 * Hour

	// 使用更标准的时间格式字符串
	DateFormat      = "2006-01-02"
	DateTimeFormat  = "2006-01-02 15:04:05"
	HourStartFormat = "2006-01-02 15:00:00"
	HourEndFormat   = "2006-01-02 15:59:59"
)

var (
	// 定义包级变量存储时区
	LocalTimezone = time.FixedZone("UTC+8", 8*60*60)
)

// 添加统一的时区转换函数
func ToLocalTime(t time.Time) time.Time {
	return t.In(LocalTimezone)
}

// 合并类似的时间获取函数
func GetDayBoundary(t time.Time, isStart bool) time.Time {
	if isStart {
		return gtime.New(t).FormatTo(layoutStart).Add(8 * time.Hour).UTC().Time
	}
	return gtime.New(t).FormatTo(layoutEnd).Add(8 * time.Hour).UTC().Time
}

// GetZeroHours 获取指定日期 0点 的时间戳, 默认当天
// 2021-11-3 00:00:00 => 1635868800000
func GetZeroHours(t time.Time) (r int64) {
	//r = time.Date(t.Year(), t.Month(), t.Day(), 0, 0, 0, 0,time.Local).Unix()
	//r = gtime.New(t).FormatTo("Y-m-d H:00:00").TimestampMilli()
	// 零点
	r = gtime.New(t).FormatTo(layoutStart).TimestampMilli()
	return
}

// GetZeroHoursTime 获取指定日期 0点 的时间 time  返回 UTC 的时间 默认当天
func GetZeroHoursTime(t time.Time) (r time.Time) {
	//r = time.Date(t.Year(), t.Month(), t.Day(), 0, 0, 0, 0,time.Local).Unix()
	//r = gtime.New(t).FormatTo("Y-m-d H:00:00").TimestampMilli()
	// 零点
	r = gtime.New(t).FormatTo(layoutStart).Add(8 * time.Hour).UTC().Time
	return
}

// GetZeroOneHoursTime 获取 指定 0点 1分钟 时间
func GetZeroOneHoursTime(t time.Time) (r time.Time) {
	//r = time.Date(t.Year(), t.Month(), t.Day(), 0, 0, 0, 0,time.Local).Unix()
	//r = gtime.New(t).FormatTo("Y-m-d H:00:00").TimestampMilli()
	// 零点1分钟
	r = gtime.New(t).FormatTo(layoutStartOne).Add(8 * time.Hour).UTC().Time
	return
}

// GetEndHours 获取指定日期 23:59:59 的时间戳
// 2021-11-3 23:59:59 => 1635955199000
func GetEndHours(t time.Time) (r int64) {
	// 23:59:59
	r = gtime.New(t).FormatTo(layoutEnd).TimestampMilli()
	return
}

// GetEndHoursTime 获取指定日期 23:59:59 的时间 time.Time
func GetEndHoursTime(t time.Time) (r time.Time) {
	// 23:59:59
	r = gtime.New(t).FormatTo(layoutEnd).Add(8 * time.Hour).UTC().Time
	return
}

// GetHoursUnix 获取指定日期 / 时 / 整点 时间戳
// Y-m-d H:00:00 ==> 1635872400000
func GetHoursUnix(t time.Time) (r int64) {
	// 整点的
	r = gtime.New(t).FormatTo(layoutHourStime).TimestampMilli()
	return
}

// GetCurHourSTime 获取 当前 整点 时间 time.Time
func GetCurHourSTime() (r time.Time) {
	r = gtime.Now().FormatTo(layoutHourStime).Add(8 * time.Hour).UTC().Time
	return
}

// GetCurHourETime 获取 当前 整点 h:59:59 时间 time.Time
func GetCurHourETime() (r time.Time) {
	r = gtime.Now().FormatTo(layoutHourEtime).Add(8 * time.Hour).UTC().Time
	return
}

// GetPreHourStime 获取 前一个小时的 整点 时间 time.Time
func GetPreHourStime() (r time.Time) {
	r = gtime.Now().FormatTo(layoutHourStime).Add(7 * time.Hour).UTC().Time
	return
}

// GetPreHourEtime 获取 前一个小时的 H:59:59 时间 time.Time
func GetPreHourEtime() (r time.Time) {
	r = gtime.Now().FormatTo(layoutHourEtime).Add(7 * time.Hour).UTC().Time
	return
}

// Get24Hours 获取指定日期 一天当中的 各个整点的时间戳 0~23点
// [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23]==>
func Get24Hours(t time.Time) g.MapIntAny {
	var temp = g.MapIntAny{}
	// 当天0点
	zero := GetZeroHours(t)
	//temp[0] = zero
	for i := 0; i < 24; i++ {
		temp[i] = zero + oneH.Milliseconds()*int64(i)
		//fmt.Println(temp[i])
	}

	return temp
}

// GetBetweenDaysMs 返回 指定时间 间隔时间戳 日期的
func GetBetweenDaysMs(start, end time.Time) []int64 {

	// 转成时间戳
	startUnix := GetZeroHours(start)
	endUnix := GetZeroHours(end)
	// day unix
	dayOne := int64(86400 * 1000)

	//i := 1
	// 求相差天数
	date := (endUnix - startUnix) / dayOne

	firstTime := startUnix
	// 当startUnix
	if startUnix > endUnix {
		//i = 2
		date = (startUnix - endUnix) / dayOne
		firstTime = endUnix
	}
	// 实始值
	arr := []int64{firstTime}
	for i := 0; i < int(date); i++ {
		firstTime = firstTime + dayOne
		arr = append(arr, firstTime)
	}

	return arr
}

// GetBetweenHoursMs 返回 指定时间 间隔时间戳 小时
// 默认 start < end
func GetBetweenHoursMs(start, end time.Time) []int64 {

	// 转成时间戳
	startUnix := GetHoursUnix(start)
	endUnix := GetHoursUnix(end)

	// Hour unix
	hourOne := int64(3600 * 1000)

	// 求相小时数
	date := (endUnix - startUnix) / hourOne

	firstTime := startUnix

	if startUnix > endUnix {
		date = (startUnix - endUnix) / hourOne
		firstTime = endUnix
	}

	// 实始值
	arr := []int64{firstTime}

	for i := 0; i < int(date); i++ {
		firstTime = firstTime + hourOne
		arr = append(arr, firstTime)
	}

	return arr
}

// GetBetweenDaysStr
// 获取 start ,end 两日期的数据
// 输入: GetBetweenDaysStr(“2019-04-27”，"2019-04-29“)
// 输出: [“2019-04-27”, “2019-04-28”, “2019-04-29”]
func GetBetweenDaysStr(start, end string) []string {
	timeLayout := layoutDay
	//loc, _ := time.LoadLocation("Asia/Shanghai")
	// 转成时间戳
	startUnix, _ := gtime.StrToTimeFormat(start, timeLayout)
	endUnix, _ := gtime.StrToTimeFormat(end, timeLayout)
	startTime := startUnix.Unix()
	endTime := endUnix.Unix()
	// 求相差天数
	date := (endTime - startTime) / 86400
	arr := []string{start}
	for i := 0; i < int(date); i++ {
		startTime = startTime + 86400
		arr = append(arr, gtime.NewFromTimeStamp(startTime).Format(timeLayout))
	}
	return arr
}

// GetBetweenDaysTime
// 获取 start ,end 两日期的数据
// 输入: GetBetweenDaysTime(“2021-11-03 00:00:00 +0000 UTC”，"2021-11-05 00:00:00 +0000 UTC“)
// 输出: [2021-11-03 00:00:00 +0000 UTC 2021-11-04 00:00:00 +0000 UTC 2021-11-05 00:00:00 +0000 UTC]
func GetBetweenDaysTime(stime, etime time.Time) []time.Time {
	//timeLayout := layoutDay
	//loc, _ := time.LoadLocation("Asia/Shanghai")
	// 转成时间戳
	startUnix := stime.Unix()
	endUnix := etime.Unix()
	//startTime := startUnix.Unix()
	//endTime := endUnix.Unix()
	//相关几天
	date := (endUnix - startUnix) / 86400

	arr := []time.Time{gtime.NewFromTimeStamp(startUnix).UTC().Time}
	for i := 0; i < int(date); i++ {
		startUnix = startUnix + 86400
		arr = append(arr, gtime.NewFromTimeStamp(startUnix).UTC().Time)
	}
	return arr

}

// GetYesterDayms  获取前一天的0点 与23:59:59的时间戳
// day 默认是当前的日期 ,可传参数
func GetYesterDayms(day time.Time) []int64 {
	var curDay time.Time
	if day.String() != "" {
		curDay = day
	} else {
		curDay = gtime.Now().Add(8 * time.Hour).UTC().Time
	}
	dayStart := gtime.New(curDay).Add(-1 * oneD).FormatTo(layoutStart).TimestampMilli()
	dayEnd := gtime.New(curDay).Add(-1 * oneD).FormatTo(layoutEnd).TimestampMilli()

	arr := []int64{dayStart, dayEnd}
	return arr
}

// GetYesterDayTime 获取指定Day的前一天0点与 23:59:59点 ,time.Time
func GetYesterDayTime(curDay time.Time) []time.Time {
	var sStr, eStr time.Time
	sStr = gtime.NewFromTime(curDay).Add(-1 * oneD).FormatTo(layoutStart).Time
	eStr = gtime.NewFromTime(curDay).Add(-1 * oneD).FormatTo(layoutEnd).Time
	//dayStart := GetUTCtime(sStr)
	//dayEnd := GetUTCtime(eStr)
	arr := []time.Time{sStr, eStr}
	return arr
}

// GetTodayDayTime 获取指定Day的0点与 23:59:59点 ,time.Time
func GetTodayDayTime(curDay time.Time) []time.Time {
	var sStr, eStr time.Time
	sStr = gtime.NewFromTime(curDay).FormatTo(layoutStart).Time
	eStr = gtime.NewFromTime(curDay).FormatTo(layoutEnd).Time
	//dayStart := GetUTCtime(sStr)
	//dayEnd := GetUTCtime(eStr)
	arr := []time.Time{sStr, eStr}
	return arr
}

// SetTimes
// 当 stime 未指定时 返回当前日期  00:00 的 time.Time
//
// day int 间隔的几天 , 当小于0 时.为几天前  当大于0 时.为几天后
func SetTimes(stime string, day int) (Stime, Etime time.Time, err error) {
	//if stime == "" {
	//	stime = gtime.Now().String()
	//}
	if stime == "" {
		stime = gtime.Now().String()
	}
	// 限制时间 7天内的, 默认
	if day == 0 {
		day = 7
	}
	if day < 0 {
		Stime = GetZeroHoursTime(gtime.New(stime).Time)
		Etime = GetEndHoursTime(gtime.New(stime).AddDate(0, 0, -day).Time)
	} else {
		Stime = GetZeroHoursTime(gtime.New(stime).AddDate(0, 0, -day).Time)
		Etime = GetEndHoursTime(gtime.New(stime).Time)
	}
	if day < -365 || day > 365 {
		return time.Time{}, time.Time{}, fmt.Errorf("day range should be [-365, 365]")
	}
	// req.Stime 2021-6-7 14:43:30 需用UTC
	return
}

// GetUTCtime 获取 utc +8 时间
// layout =
// "2018-02-09 20:46:17",
// "2018/02/09 12:00:15",
func GetUTCtime(layout string) time.Time {
	utime := gtime.NewFromStr(layout).Add(8 * time.Hour).UTC().Time
	return utime
}

// GetLocalTime 获取当前时间的 UTC +8 小时
func GetLocalTime() time.Time {
	utime := gtime.Now().Add(8 * time.Hour).UTC().Time
	return utime
}

// GetLocalTimeOut  反向输出, 因存的时候是+8, 取的时候要减8
func GetLocalTimeOut(t time.Time) time.Time {
	utime := gtime.NewFromTime(t).Add(-8 * time.Hour).UTC().Time
	return utime
}

// GetToday  返回当天的
func GetToday(layout string) string {
	utime := gtime.Now().Add(8 * time.Hour).UTC().Time
	return utime.Format(layout)
}

// WeekByDate 返回给定时间在当年的第几周
// 周数计算规则:
// 1. 每年第一周从1月1日开始计算
// 2. 如果1月1日不是周一，则该周归属上一年最后一周
// 返回值: 1-53 之间的字符串表示的周数
func WeekByDate(t time.Time) string {
	yearDay := t.YearDay()
	yearFirstDay := t.AddDate(0, 0, -yearDay+1)
	firstDayInWeek := int(yearFirstDay.Weekday())

	//今年第一周有几天
	firstWeekDays := 1
	if firstDayInWeek != 0 {
		firstWeekDays = 7 - firstDayInWeek + 1
	}
	var week int
	if yearDay <= firstWeekDays {
		week = 1
	} else {
		week = (yearDay-firstWeekDays)/7 + 2
	}
	//return fmt.Sprintf("%d第%d周", t.Year(), week)
	return fmt.Sprint(week)
}

type WeekDate struct {
	WeekTh    string
	StartTime time.Time
	EndTime   time.Time
}

// GroupByWeekDate 将开始时间和结束时间分割为周为单位
// [{2019第20周 2019-05-13 00:00:00 +0800 CST 2019-05-17 00:00:00 +0800 CST},
//
//	{2019第19周 2019-05-06 00:00:00 +0800 CST 2019-05-13 00:00:00 +0800 CST}]
func GroupByWeekDate(startTime, endTime time.Time) []WeekDate {
	weekDate := make([]WeekDate, 0)
	diffDuration := endTime.Sub(startTime)
	days := int(math.Ceil(float64(diffDuration/(time.Hour*24)))) + 1

	currentWeekDate := WeekDate{}
	currentWeekDate.WeekTh = WeekByDate(endTime)
	currentWeekDate.EndTime = endTime
	currentWeekDay := int(endTime.Weekday())
	if currentWeekDay == 0 {
		currentWeekDay = 7
	}
	currentWeekDate.StartTime = endTime.AddDate(0, 0, -currentWeekDay+1)
	nextWeekEndTime := currentWeekDate.StartTime
	weekDate = append(weekDate, currentWeekDate)

	for i := 0; i < (days-currentWeekDay)/7; i++ {
		weekData := WeekDate{}
		weekData.EndTime = nextWeekEndTime
		weekData.StartTime = nextWeekEndTime.AddDate(0, 0, -7)
		weekData.WeekTh = WeekByDate(weekData.StartTime)
		nextWeekEndTime = weekData.StartTime
		weekDate = append(weekDate, weekData)
	}

	if lastDays := (days - currentWeekDay) % 7; lastDays > 0 {
		lastData := WeekDate{}
		lastData.EndTime = nextWeekEndTime
		lastData.StartTime = nextWeekEndTime.AddDate(0, 0, -lastDays)
		lastData.WeekTh = WeekByDate(lastData.StartTime)
		weekDate = append(weekDate, lastData)
	}

	return weekDate
}

// 添加辅助函数来处理时间转换
func ConvertMongoTime(timeField interface{}) *time.Time {
	if timeField == nil {
		return nil
	}

	switch t := timeField.(type) {
	case primitive.DateTime:
		timestamp := time.Unix(int64(t)/1000, int64(t)%1000*1000000).Add(-8 * time.Hour)
		return &timestamp
	case *time.Time:
		adjusted := t.Add(-8 * time.Hour)
		return &adjusted
	case time.Time:
		adjusted := t.Add(-8 * time.Hour)
		return &adjusted
	default:
		return nil
	}
}
